package com.eflow.engine.listener;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eflow.engine.event.RejectedEventImpl;
import com.eflow.engine.handler.ProcessListenerHandler;
import com.eflow.model.domain.EflowTask;
import com.eflow.model.domain.EflowUser;
import com.eflow.model.e.BpmConst;
import com.eflow.engine.service.IEflowTaskService;
import com.eflow.model.e.DelFlagEnum;
import com.eflow.model.e.TaskStatus;
import com.eflow.model.service.IEflowUserService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEvent;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.delegate.event.FlowableProcessStartedEvent;
import org.flowable.engine.delegate.event.impl.FlowableProcessEventImpl;
import org.flowable.engine.delegate.event.impl.FlowableProcessStartedEventImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author hui se
 * @description 全局任务监听器，待办、已办处理
 * @create 2021-02-25 16:42
 **/
@Slf4j
public class GlobalTaskListener extends AbstractFlowableEngineEventListener implements BpmConst {
    @Autowired
    private IEflowTaskService eflowTaskService;
    @Autowired
    private IEflowUserService userService;
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ProcessListenerHandler processListener;

    @Override
    protected void taskCreated(FlowableEngineEntityEvent event) {
        String processInstanceId = event.getProcessInstanceId();
        //得到任务实例
        TaskEntity task = (TaskEntity)event.getEntity();
        ExecutionEntity entity = getExecutionEntity(event);
        Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
        AtomicReference<String> title=new AtomicReference<>();
        title.set(task.getVariable(VAR_PROCESS_TITLE).toString());

        processListener.handle((next)->{
            title.set(next.getTitle(task.getId(), task.getProcessInstanceId(), entity));
            return true;
        },process.getId());
        EflowTask eflowTask = getEflowTask(processInstanceId, task, title.get());
        if(eflowTaskService.count(Wrappers.lambdaQuery(EflowTask.class).eq(EflowTask::getTaskId,task.getId()))==0){
            eflowTaskService.save(eflowTask);
        }
        log.info("任务创建：{}",task.getName());
    }

    @Override
    protected void taskAssigned(FlowableEngineEntityEvent event) {
        String processInstanceId = event.getProcessInstanceId();
        //得到任务实例
        TaskEntity task = (TaskEntity)event.getEntity();
        ExecutionEntity entity = getExecutionEntity(event);
        AtomicReference<String> title=new AtomicReference<>();
        title.set(task.getVariable(VAR_PROCESS_TITLE).toString());
        Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());

        processListener.handle((next)->{
            String tempTitle = next.getTitle(task.getId(), task.getProcessInstanceId(), entity);
            if(StrUtil.isNotBlank(tempTitle)){
                title.set(tempTitle);
            }
            return true;
        },process.getId());
        EflowTask eflowTask = getEflowTask(processInstanceId, task, title.get());
        //获取审批人
        if(task.getAssignee()!=null){
            EflowUser eflowUser = userService.selectUserByUserId(task.getAssignee());
            //获取代理用户
            eflowUser = userService.selectProxyUserByUser(eflowUser);
           if(eflowUser!=null){
               eflowTask.setUserId(eflowUser.isProxy()?eflowUser.getProxyUserId():eflowUser.getUserId());
               eflowTask.setRealName(eflowUser.isProxy()?eflowUser.getProxyNickName():eflowUser.getNickName());
               eflowTask.setDeptId(eflowUser.isProxy()?eflowUser.getProxyDeptId():eflowUser.getDeptId());
               eflowTask.setDeptName(eflowUser.isProxy()?eflowUser.getProxyDeptName():eflowUser.getDeptName());
               eflowTask.setProxy(eflowUser.isProxy());
               if(eflowUser.isProxy()){
                   eflowTask.setProxyUserId(eflowUser.getUserId());
                   eflowTask.setProxyRealName(eflowUser.getNickName());
                   eflowTask.setProxyDeptId(eflowUser.getDeptId());
                   eflowTask.setProxyDeptName(eflowUser.getDeptName());
               }
           }
        }else {
            log.error("未到审批人:{}",processInstanceId);
        }

        if(eflowTaskService.count(Wrappers.lambdaQuery(EflowTask.class).eq(EflowTask::getTaskId,task.getId()))==0){
            eflowTaskService.save(eflowTask);
        }else {
            eflowTaskService.update(Wrappers.lambdaUpdate(EflowTask.class)
                    .set(EflowTask::getUserId,eflowTask.getUserId())
                    .set(EflowTask::getRealName,eflowTask.getRealName())
                    .set(EflowTask::getDeptId,eflowTask.getDeptId())
                    .set(EflowTask::getDeptName,eflowTask.getDeptName())
                    .eq(EflowTask::getTaskId,task.getId())
            );
        }
        log.info("任务审批人分配：{}->{}",task.getName(),task.getAssignee());
        if(task.getAssignee()!=null){
            processListener.handle((next)->{
                next.taskAssigned(eflowTask,task, task.getProcessInstanceId(), entity);
                return true;
            },process.getId());
        }
    }

    private ExecutionEntity getExecutionEntity(FlowableEngineEvent event) {
        ExecutionEntity entity = null;
        String executionId = event.getExecutionId();
        if (executionId != null) {
            CommandContext commandContext = CommandContextUtil.getCommandContext();
            if (commandContext != null) {
                entity = CommandContextUtil.getExecutionEntityManager(commandContext).findById(executionId);
            }
        }
        return entity;
    }

    @Override
    protected void entityDeleted(FlowableEngineEntityEvent event) {
        if(event.getEntity() instanceof TaskEntity){
            TaskEntity task=(TaskEntity)event.getEntity();
            Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
            processListener.handle((next)->{
                next.taskDeleted(task,task.getProcessInstanceId(), getExecutionEntity(event));
                return true;
            },process.getId());
        }
    }

    /**
     * 自定义事件 通过cmd调用自定义事件实现退回的监听
     * @param event
     */
    @Override
    protected void custom(FlowableEngineEvent event) {
        if(event instanceof RejectedEventImpl){
            TaskEntity task = (TaskEntity)((RejectedEventImpl)event).getEntity();
            Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
            processListener.handle((next)->{
                next.rejected(task.getId(),task.getProcessInstanceId(), getExecutionEntity(event));
                return true;
            },process.getId());
        }
    }

    @Override
    protected void taskCompleted(FlowableEngineEntityEvent event) {
        //得到任务实例
        TaskEntity task = (TaskEntity)event.getEntity();
        log.info("任务提交：{}->{}",task.getName());
        Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
        processListener.handle((next)->{
            next.completed(task.getId(),task.getProcessInstanceId(), getExecutionEntity(event));
            return true;
        },process.getId());
    }

    private EflowTask getEflowTask(String processInstanceId, TaskEntity task,String title) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).includeProcessVariables().singleResult();
        Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId());
        return new EflowTask().setTaskId(task.getId())
                .setTaskName(task.getName())
                .setStartDate(new Date())
                .setProcessName(process.getName())
                .setActivityId(task.getTaskDefinitionKey())
                .setBusinessId(processInstance.getBusinessKey())
                .setBusinessType(process.getId())
                .setStatus(TaskStatus.WAIT)
                .setDelFlag(DelFlagEnum.NORMAL.getCode())
                .setProcessDefinitionId(processInstance.getProcessDefinitionId())
                .setProcessInstanceId(processInstance.getProcessInstanceId())
                .setTitle(title);
    }

    @Override
    protected void processStarted(FlowableProcessStartedEvent event) {
        FlowableProcessStartedEventImpl startedEvent = (FlowableProcessStartedEventImpl) event;
        Process process = ProcessDefinitionUtil.getProcess(startedEvent.getProcessDefinitionId());
        processListener.handle((next)->{
            next.start(startedEvent.getProcessInstanceId(),startedEvent.getExecution(),startedEvent.getVariables());
            return true;
        },process.getId());
    }

    @Override
    protected void processCancelled(FlowableCancelledEvent event) {
        Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
        processListener.handle((next)->{
            next.processDelete(event.getProcessInstanceId(),getExecutionEntity(event));
            return true;
        },process.getId());
    }

    @Override
    protected void processCompleted(FlowableEngineEntityEvent event) {
        FlowableProcessEventImpl entityEvent= (FlowableProcessEventImpl)event;
        Process process = ProcessDefinitionUtil.getProcess(event.getProcessDefinitionId());
        processListener.handle((next)->{
            next.end(entityEvent.getProcessInstanceId(),entityEvent.getExecution());
            return true;
        },process.getId());
    }
}
